import 'package:dio/dio.dart';
import 'package:sainbainu/common/http/config.dart';
import 'package:sainbainu/common/http/exception.dart';
import 'package:sainbainu/common/http/interceptor.dart';
import 'package:sainbainu/common/http/retry_token_interceptor.dart';
import 'package:sainbainu/common/values/settings.dart';

// http请求类
class HttpRequest {
  // 初始化一个单例实例
  static final HttpRequest _instance = HttpRequest._internal();

  // 工厂构造方法 外部调用 HttpRequest httpRequest = new HttpRequest()
  factory HttpRequest() => _instance;

  late Dio dio;

  // 内部构造方法
  HttpRequest._internal() {
    BaseOptions baseOptions = BaseOptions(
      baseUrl: HttpConfig.baseUrl,
      connectTimeout: HttpConfig.connectTimeout,
      receiveTimeout: HttpConfig.receiveTimeout,
    );

    // 没有实例 则创建之
    dio = Dio(baseOptions);

    // 添加拦截器
    dio.interceptors.add(RetryTokenInterceptor());
    dio.interceptors.add(HttpInterceptor());
    if (AppSettings.isDebug) {
      dio.interceptors.add(
        LogInterceptor(requestBody: true, responseBody: true),
      );
    }
  }

  /// 初始化公共属性 如果需要覆盖原配置项 就调用它
  ///
  /// [baseUrl] 地址前缀
  /// [connectTimeout] 连接超时赶时间
  /// [receiveTimeout] 接收超时赶时间
  /// [headers] 请求头
  /// [interceptors] 基础拦截器
  void init({
    String baseUrl = '',
    Duration? connectTimeout,
    Duration? receiveTimeout,
    Map<String, dynamic>? headers,
    List<Interceptor>? interceptors,
  }) {
    dio.options.baseUrl = baseUrl;
    dio.options.connectTimeout = connectTimeout ?? HttpConfig.connectTimeout;
    dio.options.receiveTimeout = receiveTimeout ?? HttpConfig.receiveTimeout;
    dio.options.headers = headers;
    if (interceptors != null && interceptors.isNotEmpty) {
      dio.interceptors.addAll(interceptors);
    }
  }

  /// 设置请求头
  void setHeaders(Map<String, dynamic> headers) {
    dio.options.headers.addAll(headers);
  }

  final CancelToken _cancelToken = CancelToken();
  /*
   * 取消请求
   *
   * 同一个cancel token 可以用于多个请求
   * 当一个cancel token取消时，所有使用该cancel token的请求都会被取消。
   * 所以参数可选
   */
  void cancelRequests({CancelToken? token}) {
    token ?? _cancelToken.cancel("cancelled");
  }

  /// restful get 操作
  Future<Response> get(
    String path, {
    Map<String, dynamic>? params,
    Options? options,
    CancelToken? cancelToken,
    ProgressCallback? onReceiveProgress,
  }) async {
    try {
      Response response = await dio.get(
        path,
        queryParameters: params,
        options: options,
        cancelToken: cancelToken ?? _cancelToken,
        onReceiveProgress: onReceiveProgress,
      );
      return response;
    } on DioException catch (e) {
      CustomHttpException httpException = e.error as CustomHttpException;
      throw httpException;
    }
  }

  /// restful post 操作
  Future<Response> post(
    String path, {
    Map<String, dynamic>? params,
    dynamic data,
    Options? options,
    CancelToken? cancelToken,
    ProgressCallback? onSendProgress,
    ProgressCallback? onReceiveProgress,
  }) async {
    try {
      Response response = await dio.post(
        path,
        data: data,
        queryParameters: params,
        options: options,
        cancelToken: cancelToken ?? _cancelToken,
        onSendProgress: onSendProgress,
        onReceiveProgress: onReceiveProgress,
      );
      return response;
    } on DioException catch (e) {
      CustomHttpException httpException = e.error as CustomHttpException;
      throw httpException;
    }
  }

  /// restful post form 表单提交操作
  Future<Response> postForm(
    String path, {
    Map<String, dynamic>? params,
    Options? options,
    CancelToken? cancelToken,
    ProgressCallback? onSendProgress,
    ProgressCallback? onReceiveProgress,
  }) async {
    try {
      Response response = await dio.post(
        path,
        data: FormData.fromMap(params ?? {}),
        options: options,
        cancelToken: cancelToken ?? _cancelToken,
        onSendProgress: onSendProgress,
        onReceiveProgress: onReceiveProgress,
      );
      return response;
    } on DioException catch (e) {
      CustomHttpException httpException = e.error as CustomHttpException;
      throw httpException;
    }
  }

  /// 下载
  Future<Response> download(
    String urlPath,
    dynamic savePath, {
    Map<String, dynamic>? queryParameters,
    CancelToken? cancelToken,
    bool deleteOnError = true,
    String lengthHeader = Headers.contentLengthHeader,
    Object? data,
    Options? options,
    ProgressCallback? onReceiveProgress,
  }) async {
    try {
      Response response = await dio.download(
        urlPath,
        savePath,
        queryParameters: queryParameters,
        cancelToken: cancelToken ?? _cancelToken,
        deleteOnError: deleteOnError,
        lengthHeader: lengthHeader,
        data: data,
        options: options,
        onReceiveProgress: onReceiveProgress,
      );
      return response;
    } on DioException catch (e) {
      CustomHttpException httpException = e.error as CustomHttpException;
      throw httpException;
    }
  }

  /// fetch
  Future<Response> fetch(RequestOptions requestOptions) async {
    try {
      Response response = await dio.fetch(requestOptions);
      return response;
    } on DioException catch (e) {
      CustomHttpException httpException = e.error as CustomHttpException;
      throw httpException;
    }
  }
}
